Skip to content

feat(hcg): add spec-coverage check script (standards#100 §1.5)#230

Merged
hyperpolymath merged 1 commit into
mainfrom
feat/hcg-spec-coverage-check
Jun 21, 2026
Merged

feat(hcg): add spec-coverage check script (standards#100 §1.5)#230
hyperpolymath merged 1 commit into
mainfrom
feat/hcg-spec-coverage-check

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Adds scripts/hcg-spec-coverage-check.sh: a static, source-only audit that asserts every HTTP route declared in docs/specification/openapi.yaml is covered by at least one rule in the HCG live Verb Governance Spec (config/gateway-policy-boj.yaml).

Companion / complement to PR #228's hcg-surface-drift-check.sh. The two scripts bracket the contract §8 declared-surface invariant from both directions:

Script Invariant Catches
hcg-surface-drift-check.sh (#228) wired (router.ex) ⊆ policy policy lag behind wiring
hcg-spec-coverage-check.sh (this PR) declared (openapi.yaml) ⊆ policy policy lag behind the spec

Contract §8 (docs/integration/http-capability-gateway-boj-contract.md) is explicit: "the Verb Governance Spec governs the declared surface (openapi.yaml), not only the currently-wired subset. Declared-but-unimplemented routes are still classified in the policy so that when the gnosis handler grows them they are governed from day one rather than silently exposed." The live policy header carries the cross-check statement ("Surface source: docs/specification/openapi.yaml, cross-checked against elixir/lib/boj_rest/router.ex"); PR #228 made the router half machine-checkable, this PR makes the openapi half machine-checkable. Together they make the entire §1.5 re-verification stamp executable.

Without this check the risk is concrete: someone adds a new path to openapi.yaml without a corresponding policy rule. The surface-drift check does not catch it (the route is not yet wired in router.ex). The day the route is wired, the surface-drift gate fires — but by then the operator has to either (a) ship the wiring with a default-deny in production for a route that should be live or (b) hold the wiring PR until the policy catches up. Catching the gap at spec-edit time avoids both, with no procedural cost above running the existing CI gate.

What the script does

  1. Extracts (verb, path-template) tuples from the paths: section of docs/specification/openapi.yaml — path entries at exactly 2-space indent, HTTP operations (get/post/put/delete/patch/head/options) at exactly 4-space indent under each path. Other keys at 4-space indent (parameters/summary/description/tags/...) are metadata, not operations, and are skipped.
  2. Extracts (verb, path-pattern) tuples from config/gateway-policy-boj.yaml using the identical extraction block that hcg-surface-drift-check.sh uses, so the two scripts cannot drift in how they read the policy.
  3. For each declared route, concretises {name}-style placeholders with a known probe segment (probe, shared with the smoke + surface-drift scripts so a future regex tightening fails all three in lock-step) and asserts at least one policy rule covers it: literal equality for non-regex paths; ERE grep -E match against the concrete URL for ^… regex paths. The declared verb must be in the policy rule's verb list.
  4. Exit 0 on no gap, 1 on gap detected, 64 on bad usage.

What this PR does NOT do

Channel state note

This session could not read hyperpolymath/standards#91 / #100 (the session's repository scope is restricted to http-capability-gateway and boj-server), so the brief's instructed status comment on standards#91 could not be posted. State was reconstructed from the canonical sources in this repo (ADR-0004, the integration plan, the audit, the rollout runbook, the live policy, the openapi spec, and the merged-PR commit history) plus the current main of both in-scope repos. The analysis: Phase A/B/C/D are closed (artefacts merged, runbook §1.2 and the Phase-D status note in the runbook header confirm); Phase E (standards#100) is the only open phase; all remaining §1 checklist items are owner-driven (!OWNER: placeholders, D-4 rebaseline workflow_dispatch, cerro-torre .ctp signing, the §6.4 Trustfile flip). This PR advances Phase E §1.5 ("Gateway-side prerequisites") by converting one half of the declared-surface invariant into an executable artefact, mirroring exactly the script-first split of #228.

Test plan

  • Run the script on this branch's working tree: bash scripts/hcg-spec-coverage-check.sh — expect exit 0, "OK: every openapi-declared route is covered by at least one policy rule." with Declared (openapi) routes: 26 and Policy (verb,path) rules: 28.
  • Run bash scripts/hcg-spec-coverage-check.sh -v — expect the same exit 0 plus a Matched: block listing each of the 26 declared routes against its policy rule (literal /health → literal rule; /cartridge/{name}/invoke^/cartridge/[A-Za-z0-9_.-]+/invoke$ regex; /grpc/{service}/{method} → two-segment regex; /umoja/peers matches both GET and POST rules; etc.).
  • Synthetic gap test: build a temporary openapi.yaml containing a single declared path with no policy rule and run OPENAPI_FILE=... bash scripts/hcg-spec-coverage-check.sh — expect exit 1 with the route listed under GAP:. (Verified locally on this branch.)
  • Confirm shellcheck scripts/hcg-spec-coverage-check.sh produces only the same SC1001 info note that scripts/hcg-surface-drift-check.sh produces today (the \^ escape inside a case pattern is intentional and matches the sibling script's posture exactly).
  • Confirm SPDX header + Owner copyright match the canonical estate format (matches scripts/hcg-surface-drift-check.sh's header shape).
  • Verify scripts/check-shebang-first.sh is still green with the new file present.
  • Verify no Hypatia / governance / spdx gates fire on the new script file.

Refs hyperpolymath/standards#91
Refs hyperpolymath/standards#100

🤖 Generated with Claude Code


Generated by Claude Code

Adds `scripts/hcg-spec-coverage-check.sh`: a static, source-only audit
that asserts every HTTP route declared in
`docs/specification/openapi.yaml` is covered by at least one rule in
the HCG live Verb Governance Spec (`config/gateway-policy-boj.yaml`).

Companion / complement to PR #228's `hcg-surface-drift-check.sh`. The
two scripts bracket the contract §8 declared-surface invariant from
both directions:

  surface-drift     wired (router.ex) ⊆ policy   — catches policy lag
                                                   behind wiring
  spec-coverage     declared (openapi.yaml) ⊆ policy
                                                 — catches policy lag
                                                   behind the spec

Contract §8
(`docs/integration/http-capability-gateway-boj-contract.md`) is explicit:
"the Verb Governance Spec governs the declared surface (openapi.yaml),
not only the currently-wired subset. Declared-but-unimplemented routes
are still classified in the policy so that when the gnosis handler
grows them they are governed from day one rather than silently
exposed." The live policy header already carries the cross-check
statement ("Surface source: docs/specification/openapi.yaml,
cross-checked against elixir/lib/boj_rest/router.ex"); PR #228 made the
router half machine-checkable, this PR makes the openapi half
machine-checkable. Together they make the entire §1.5 re-verification
stamp executable.

Without this check the risk is concrete: someone adds a new path to
`openapi.yaml` without a corresponding policy rule. The
surface-drift check does not catch it (the route is not yet wired in
`router.ex`). The day the route is wired, the surface-drift gate fires
— but at that point the operator has to either (a) ship the wiring
with a default-deny in production for a route that should be live or
(b) hold the wiring PR until the policy catches up. Catching the gap
at spec-edit time avoids both, with no procedural cost above running
the existing CI gate.

### What the script does

1. Extracts `(verb, path-template)` tuples from the `paths:` section
   of `docs/specification/openapi.yaml` — path entries at exactly
   2-space indent, HTTP operations (get/post/put/delete/patch/head/
   options) at exactly 4-space indent under each path. Other keys at
   4-space indent (parameters/summary/description/tags/...) are
   metadata, not operations, and are skipped.
2. Extracts `(verb, path-pattern)` tuples from
   `config/gateway-policy-boj.yaml` using the identical extraction
   block that `hcg-surface-drift-check.sh` uses, so the two scripts
   cannot drift in how they read the policy.
3. For each declared route, concretises `{name}`-style placeholders
   with a known probe segment (`probe`, shared with the smoke +
   surface-drift scripts so a future regex tightening fails all three
   in lock-step) and asserts at least one policy rule covers it:
   literal equality for non-regex paths; ERE `grep -E` match against
   the concrete URL for `^…` regex paths. The declared verb must be
   in the policy rule's verb list.
4. Exit 0 on no gap, 1 on gap detected, 64 on bad usage.

### What this PR does NOT do

- Does **not** modify the rollout runbook §1.5 or the contract §8.
  Adoption as the §1.5 declared-surface check is a separate, owner-
  driven PR; this PR lands the artefact only so the runbook update is
  a one-line wiring change. Matches the §228-then-runbook split.
- Does **not** wire the script into CI. Boj-server's CI discipline
  (`docs/wikis/CI-and-Required-Checks.adoc` / `.claude/CLAUDE.md`)
  requires path-filtered required checks to use the "always-trigger +
  changes job" pattern; a CI wiring PR should follow that pattern,
  matching the §228 → §229 split. Out of scope here.
- Does **not** modify the openapi.yaml or the policy. On this branch
  the script reports OK against today's surface — every one of the
  26 (verb, path) pairs declared in openapi.yaml has a matching rule
  among the 28 (verb, path) rules in the live policy. The 2-rule
  surplus is the policy's coverage of routes the openapi.yaml does
  not declare (notably `/.well-known/boj-node-pubkey`, which the
  router wires but the spec does not yet enumerate); the script
  intentionally does not penalise that direction — see the script's
  "Limitations" header.
- Does **not** pre-empt the §6.4 Trustfile flip
  (`tier_2_gateway.status` stays `PENDING`).
- Per single-lane HCG channel discipline (pattern set in
  `http-capability-gateway` PRs #10, #11, #12, #14, #22, #26, #30,
  #38 and `boj-server` PRs #78, #90, #106, #168, #173, #207, #208,
  #210, #215, #222, #224, #226, #228, #229): joint-close is owner-
  only. **This PR refs but does not close `standards#100`.**

### Channel state note

This session could not read `hyperpolymath/standards#91` / `#100`
(the session's repository scope is restricted to
`http-capability-gateway` and `boj-server`), so the brief's instructed
status comment on `standards#91` could not be posted. State was
reconstructed from the canonical sources in this repo (ADR-0004, the
integration plan, the audit, the rollout runbook, the live policy, the
openapi spec, and the merged-PR commit history) plus the current
`main` of both in-scope repos. The analysis: Phase A/B/C/D are closed
(artefacts merged, runbook §1.2 and the Phase-D status note in the
runbook header confirm); Phase E (`standards#100`) is the only open
phase; all remaining §1 checklist items are owner-driven
(`!OWNER:` placeholders, D-4 rebaseline workflow_dispatch, cerro-torre
`.ctp` signing, the §6.4 Trustfile flip). This PR advances Phase E
§1.5 ("Gateway-side prerequisites") by converting one half of the
declared-surface invariant into an executable artefact, mirroring
exactly the script-first split of #228.

Refs hyperpolymath/standards#91
Refs hyperpolymath/standards#100

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 215 issues detected

Severity Count
🔴 Critical 15
🟠 High 129
🟡 Medium 71

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in scorecard-enforcer.yml",
    "type": "missing_timeout_minutes",
    "file": "scorecard-enforcer.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Issue in scorecard-enforcer.yml",
    "type": "scorecard_publish_with_run_step",
    "file": "scorecard-enforcer.yml",
    "action": "split_scorecard_publish_job",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in instant-sync.yml",
    "type": "secret_action_without_presence_gate",
    "file": "instant-sync.yml",
    "action": "peter-evans/repository-dispatch",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in codeql.yml",
    "type": "codeql_missing_actions_language",
    "file": "codeql.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath marked this pull request as ready for review June 21, 2026 06:19
@hyperpolymath hyperpolymath merged commit 567f244 into main Jun 21, 2026
37 checks passed
@hyperpolymath hyperpolymath deleted the feat/hcg-spec-coverage-check branch June 21, 2026 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant